﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Grafo;
using System.Collections;

namespace Modelo
{
    public class Simulacion
    {
        //----------------stopid--nombrelargo-----
        private Dictionary<string, string> _estaciones;
        //---------------
        private Dictionary<string, Estacion> _estacionesDef;
        //----------------lineaid--nombre
        private Dictionary<string, string> _lineas;
        private Dictionary<string, string> _arcosLineas;
        private List<Ruta> _rutasDef;
        private List<Grafo<string>> _rutas;
        private List<Grafo<Estacion>> _rutasGrafoEstacion;
        private List<String[]> _estructuraOrientacion;
        private List<Bus> _busesBaseSemana, _busesBaseSabado, _busesBaseDomingo;
        private List<List<string>> _mejoresRutas;
        //private Dictionary<string, ArrayList> lineasSimulacion;
        private int _capacidadParametro = 100;
        private int _capacidadBuses = 0;
        private int _sobrecupo = 0;
        private int _placas = 0;
        private int _diaTemporal;


        public Simulacion()
        {
            _estaciones = new Dictionary<string, string>();
            _lineas = new Dictionary<string, string>();
            _arcosLineas = new Dictionary<string, string>();
            _rutasDef = new List<Ruta>();
            //diccionario con las estaciones def.-nombreestacion-la estacion def.
            _estacionesDef = new Dictionary<string, Estacion>();
            _estructuraOrientacion = new List<String[]>();
            _busesBaseSemana = new List<Bus>();
            _busesBaseSabado = new List<Bus>();
            _busesBaseDomingo = new List<Bus>();
        }

        public void setCapacidadParametro(int capacidad)
        {
            _capacidadParametro = capacidad;
        }

        public Dictionary<string, string> darEstaciones()
        {
            return _estaciones;
        }

        public Dictionary<string, string> darLineas()
        {
            return _lineas;
        }

        public List<Grafo<string>> darRutas()
        {
            return _rutas;
        }

        public void agregarEstacion(string stopId, string nombreLargo)
        {
            _estaciones.Add(stopId, nombreLargo);
        }

        public void agregarLinea(string lineaId, string nombre)
        {
            _lineas.Add(lineaId, nombre);
        }

        public void inicializarRutas()
        {
            List<string> elementosLinea = _lineas.Keys.ToList();
            int tamano = elementosLinea.Count;
            _rutas = new List<Grafo<string>>(tamano);
            _rutasGrafoEstacion = new List<Grafo<Estacion>>(tamano);
            for (int contador = 0; contador < tamano; contador++)
            {
                string idGrafo = elementosLinea[contador];
                Grafo<string> temporal = new Grafo<string>(true, idGrafo);
                _rutas.Add(temporal);
                Grafo<Estacion> temporal2 = new Grafo<Estacion>(true, idGrafo);
                _rutasGrafoEstacion.Add(temporal2);
            }
        }

        //Este metodo agrega a lista de rutas(posicision:depende de la posicion en que este una linea en la lista de lines),
        //los vertices(estaciones) de los grafos que representan la ruta que sigue una linea
        public void agregarStopLinea(string idLinea, string idStop, string orientacion)
        {
            foreach (Grafo<string> temporal in _rutas)
            {
                if (temporal.darId().Equals(idLinea))
                {
                    temporal.addVertice(_estaciones[idStop]);
                }
            }
            string clave = idLinea + ";" + orientacion;
            string[] temp = { clave, idStop };
            _estructuraOrientacion.Add(temp);
        }

        //este metodo agrega los arcos de los grafos que estan en la lista de rutas
        public void agregarArcos(string idStart, string idStop, string tiempo, string idArco)
        {
            string idLinea = "";
            string estacionInicio = "";
            string estacionFin = "";
            try
            {
                idLinea = _arcosLineas[idArco];
                estacionInicio = _estaciones[idStart];
                estacionFin = _estaciones[idStop];
                if (estacionInicio.Equals(string.Empty) || idLinea.Equals(string.Empty) || estacionFin.Equals(string.Empty))
                { // error, no encuentra la linea en su respectivo diccionario.
                    //Console.WriteLine("error - no se encontro la linea buscada o las estaciones de inicio o fin");
                }
                else
                {
                    foreach (Grafo<string> temporal in _rutas)
                    {
                        if (temporal.darId().Equals(idLinea))
                        {
                            double demora = double.Parse(tiempo);
                            temporal.addArista(estacionInicio, estacionFin, demora);
                        }
                    }

                }
            }
            catch (Exception) { }
        }

        public void agregarArcoLinea(string idArco, string idLinea)
        {
            _arcosLineas.Add(idArco, idLinea);
        }

        public void crearEstacionesDefinitivas(string longName, string gpsX, string gpsY)
        {
            Estacion def = new Estacion(longName, _capacidadParametro, gpsX, gpsY);
            _estacionesDef.Add(longName, def);
        }

        public void generarInstanciasFinales()
        {
            // antes que todo, hay que actualizar las instancias de estacion
            // dandoles las rutas que pasan por cada estacion.            
            foreach (Estacion estacionTemporal in _estacionesDef.Values)
            {
                string nombre = estacionTemporal.darNombre();
                List<string> rutasT = new List<string>();
                // recorremos rutas y vemos en que rutas(grafos) se encuentra esta estacion.
                foreach (Grafo<string> rutaT in _rutas)
                {
                    foreach (string estacion in rutaT.darVertices())
                    {
                        if (estacion.Equals(rutaT.darId()))
                        {
                            rutasT.Add(rutaT.darId());
                        }
                    }
                }
                estacionTemporal.setRutas(rutasT.ToArray());
            }
            // empezamos a construir el grafo con instancias definitivas.
            int tamano = _rutas.Count();
            // primero trabajamos con _rutaGrafoEstacion
            for (int contadorRuta = 0; contadorRuta < tamano; contadorRuta++)
            {
                Grafo<string> grafoTemporal = _rutas[contadorRuta];//sacamos una ruta (grafo)
                //saco vertices string
                string[] verticesString = grafoTemporal.darVertices().ToArray();
                //pasamos de string a Estacion y la agregamos a _rutasGrafoEstacion
                foreach (string temporalString in verticesString)
                {
                    Estacion nuevaEstacion = _estacionesDef[temporalString];
                    _rutasGrafoEstacion[contadorRuta].addVertice(nuevaEstacion);
                }
                // ahora nos encargamos de las aristas
                List<Object[]> aristasT = grafoTemporal.darAristas();
                foreach (Object[] temporalArista in aristasT)
                {
                    string inicio = (string)temporalArista[0];
                    string fin = (string)temporalArista[1];
                    double peso = (double)temporalArista[2];
                    Estacion inicioT = _estacionesDef[inicio];
                    Estacion finT = _estacionesDef[fin];
                    _rutasGrafoEstacion[contadorRuta].addArista(inicioT, finT, peso);
                }
            } // ya se genero un grafo con objetos Estacion.
            // empezamos a trabajar con instancias de Ruta.
            _rutasDef = new List<Ruta>(tamano);
            string idRuta = "";
            string nombreRuta = "";

            foreach (Grafo<Estacion> graf in _rutasGrafoEstacion)
            {
                idRuta = graf.darId();
                nombreRuta = _lineas[idRuta];
                List<String> paradasIda = new List<string>();
                List<String> paradasVuelta = new List<string>();
                // vamos a identificar cuales son las paradas de ida y cuales de venida
                foreach (string[] conjunto in _estructuraOrientacion)
                {
                    // recuperar idLinea y orientacion
                    string clave = conjunto[0];
                    string[] claveSeparada = clave.Split(';');
                    string lineaC = claveSeparada[0];
                    string orientacion = claveSeparada[1];
                    if (lineaC.Equals(idRuta))
                    {
                        if (orientacion.Equals("0"))
                        {// sentido 0
                            paradasIda.Add(conjunto[1]);
                        }
                        else
                        {//sentido 1
                            paradasVuelta.Add(conjunto[1]);
                        }
                    }
                }
                Ruta rutaTemporal = new Ruta(idRuta, nombreRuta, paradasIda, paradasVuelta, graf);
                _rutasDef.Add(rutaTemporal);
            }
        }

        public List<Estacion> darEstacionesDefinitivas()
        {
            List<Estacion> salida = new List<Estacion>();
            salida = _estacionesDef.Values.ToList();
            //foreach (KeyValuePair<string, Estacion> estacionTemporal in _estacionesDef)
            //    {
            //    salida.Add(estacionTemporal.Value);
            //}
            return salida;
        }

        public List<String> darMejorRutaATomar(string o, string d)
        {
            int index=dondeEncontrarMejorRuta(o,d);
            int cont = -1;
            foreach(List<String> r in _mejoresRutas){
                cont++;
                if(cont==index){
                    return r;
                }
            }
            return null;
        }
        public Grafo<String> crearSuperGrafo(int tipo)
        {
            Grafo<String> superGrafo = new Grafo<String>(true,"generico");
            
            foreach (KeyValuePair<string, string> estacion in _estaciones)
            {
                superGrafo.addVertice(estacion.Value); 
            }

            if (tipo == 1)
            {
                foreach (Grafo<String> Ruta in _rutas)
                {
                    String aristas = Ruta.printAristas();
                    String cadena = aristas.Replace("<", " ");
                    String[] valores = cadena.Split('>');
                    for (int i = 0; i <= valores.Length; i++)
                    {
                        String contenido = valores[i].Trim();
                        String[] tripla = contenido.Split(',');
                        String vetice1 = tripla[0].Trim();
                        String vetice2 = tripla[1].Trim();
                        String peso = tripla[2].Trim();
                        double distancia = Convert.ToDouble(peso);
                        superGrafo.addArista(vetice1, vetice2, distancia);
                    }
                }
                if (tipo == 2)
                {
                    int pos = -1;
                    foreach (Grafo<String> Ruta in _rutas)
                    {
                        pos++;
                        String aristas = Ruta.printAristas();
                        String cadena = aristas.Replace("<", " ");
                        String[] valores = cadena.Split('>');
                        for (int i = 0; i <= valores.Length; i++)
                        {
                            String contenido = valores[i].Trim();
                            String[] tripla = contenido.Split(',');
                            String vetice1 = tripla[0].Trim();
                            String vetice2 = tripla[1].Trim();
                            int index = -1;
                            string valor ="";
                            foreach (KeyValuePair<string, string> Par in _lineas)
                            {
                                index++;
                                if (index==pos)
                                {
                                    valor = Par.Key;
                                    break;
            }
                            }
                            int idLinea = Convert.ToInt32(valor);
                            superGrafo.addArista(vetice1, vetice2, pos);
                        }
                    }
                }
            }
            return superGrafo;
        }

        //Este metodo permite calcular las posibilidades de ruta que un pasajero pueda tener
        public List<string> calcularPosibilidades()
        {
            List<string> posibilidades= new List<string>();
            foreach (KeyValuePair<string, string> estacion in _estaciones)
            {
                string i=estacion.Value;
                foreach (KeyValuePair<string, string> estacion2 in _estaciones)
                {
                    string f = estacion2.Value;
                    if(!i.Equals(f)){
                        String posibilidad = i+";"+f;
                        posibilidades.Add(posibilidad);
                    }
                }
            }
            return posibilidades;
        }

        // Este metodo permite calcular las mejores rutas que las personas pueden tomar 
        //llenando la lista de mejores rutas con grafos donde esta la informacion de las
        //lineas tomadas.
        public void calcularMejoresRutas()
        {
            Grafo<String> superGrafo1 = crearSuperGrafo(1);
            Grafo<String> superGrafo2 = crearSuperGrafo(2);
            List<string> respuesta=calcularPosibilidades();
            List<string> temporal = new List<string>();
            foreach (String posibilidad in respuesta)
            {
                String[] inicioFin = posibilidad.Split(';');
                string rutaASeguir=dijkstra(superGrafo1,inicioFin[0], inicioFin[1]);
                String[] mejorRuta = rutaASeguir.Split(';');
                for (int i = 0; i < mejorRuta.Length;i++ )
                {
                    string est1=mejorRuta[i];
                    int val = i + 1;
                    if (!(val== (mejorRuta.Length-1)))
                    {
                        string est2 = mejorRuta[i + 1];
                        double peso = superGrafo2.darPesoArista(est1, est2);
                        int p = Convert.ToInt32(peso);
                        String pes = "" + p;
                        string nombreRuta = _lineas[pes];
                        temporal.Add(nombreRuta);
                    }
                }
                _mejoresRutas.Add(temporal);
                temporal.Clear();
            }
        }

        public string dijkstra(string estInicio, string estFin)
        {
            Ruta ruta = null;
            foreach (Ruta var in _rutasDef)
            {
                if (var.darEstacion(estInicio) != null && var.darEstacion(estFin) != null)
                {
                    ruta = var;
                    break;
                }
            }
            if (ruta != null)
            {
                return ruta.darNombre();
            }
            else
            {
                Ruta rut = null;
                foreach (Ruta var in _rutasDef)
                {
                    if (var.darEstacion(estFin) != null)
                    {
                        rut = var;
                        break;
                    }
                }
                foreach (Ruta rutas in _rutasDef)
                {
                    if (rutas.darEstacion(estInicio) != null)
                    {
                        for (int i = 0; i < rutas.darGrafoParadas().darVertices().Count; i++)
                        {
                            if (rut.darEstacion(rutas.darGrafoParadas().darVertices()[i].darNombre()) != null)
                            {
                                return rutas.darNombre() + "-" + rutas.darGrafoParadas().darVertices()[i].darNombre() + "-" + rut.darNombre();
                            }
                        }
                    }
                }
            }
        }

        //Este metodo me indica la posicion de la mejor ruta en la lista de las mejores rutas partiendo de las combinaciones
        //de las posibles rutas
        public int dondeEncontrarMejorRuta(string estacionInicio,string estacionFinal)
        {
            int posInicio=darPosicionEstacionEnLista(estacionInicio);
            int posfinal = darPosicionEstacionEnLista(estacionFinal);
            int pos = 0;
            int numeroEstaciones=_estaciones.Count;
            int saltos =numeroEstaciones-1;
            int iniciobusqueda = (posInicio * saltos)-saltos;
            pos = posInicio + (posfinal - numeroEstaciones-2);
            return pos;
        }

        //Este metodo retorna la posicion que se encuentra el valor en la lista de estaciones
        //temporales
        public int darPosicionEstacionEnLista(string nombreEstacion)
        {
            int pos = 0; 
            foreach (KeyValuePair<string, string> estacion in _estaciones)
        {
                pos++;
                if (estacion.Value.Equals(nombreEstacion))
            {                
                    break;
                }
            }
            return pos;
        }
       
       
        public List<string> darNombresLineas()
        {
            List<string> salida = new List<string>();
            foreach (KeyValuePair<string, string> nombre in _lineas)
            {
                salida.Add(nombre.Value);
            }
            return salida;
        }

        public string[] darParadasRuta(string nombreRuta)
        {
            List<string> salida = new List<string>();
            string idLinea = "";

            foreach (KeyValuePair<string, string> idd in _lineas)
            {
                if (idd.Value.Equals(nombreRuta))
                {
                    idLinea = idd.Key;
                }
            }
            foreach (Grafo<string> temporal in _rutas)
            {
                if (temporal.darId().Equals(idLinea))
                {
                    salida = temporal.darVertices();
                    //temporal
                }
            }
            return salida.ToArray();
        }

        public List<Ruta> darRutasDef()
        {
            return _rutasDef;
        }

        public List<Grafo<Estacion>> darEstacionesRutas()
        {
            return _rutasGrafoEstacion;
        }

        public void agregarFrecuencia(string linea, string dia, string frecuencia, string flota, string velocidad)
        {
            //dia 0 - entre semana
            //dia 1 - sabado
            //dia 2 - domingo           
            int _dia = int.Parse(dia);
            _placas++;
            string _placa = "A" + _placas;
            double _velocidad = double.Parse(velocidad);
            int _flota = int.Parse(flota);
            double _frecuencia = double.Parse(frecuencia);
            Ruta ruta = buscarRuta(linea);
            Bus temporal = new Bus(_placa, 0, _capacidadBuses, _sobrecupo, _velocidad, ruta, _flota, _frecuencia);
            switch (_dia)
            {
                case 0: // entre semana
                    _busesBaseSemana.Add(temporal);
                    break;

                case 1: // sabado
                    _busesBaseSabado.Add(temporal);
                    break;

                case 2: //domingo
                    _busesBaseDomingo.Add(temporal);
                    break;
                default:
                    break;
            }
        }

        private Ruta buscarRuta(string linea)
        {
            Ruta salida = null;
            string nombreT = "";
            foreach (Ruta rutaT in _rutasDef)
            {
                nombreT = rutaT.darNombre();
                if (nombreT.Equals("\"" + linea.ToUpper() + "\""))
                {
                    salida = rutaT;
                    break;
                }
            }
            return salida;
        }

        public void setParametrosBuses(int capacidad, int sobrecupo)
        {
            _capacidadBuses = capacidad;
            _sobrecupo = sobrecupo;
        }

        public void setParametrosDia(int dia)
        {
            _diaTemporal = dia;
        }

        //Este método devuelve la lista correcta dependiendo del día.
        public List<Bus> darBusesPorDia()
        {
            if (_diaTemporal == 0)
            {
                return _busesBaseSemana;
            }
            if (_diaTemporal == 1)
            {
                return _busesBaseSabado;
            }
            else
            {
                return _busesBaseDomingo;
            }
        }

        public string buscarIdEstacion(string nombreEst)
        {
            foreach (KeyValuePair<string, string> par in _estaciones)
            {
                if (par.Key == nombreEst)
                {
                    string resp = par.Value;
                    return resp;
                    {
                    }
                }
            }
            return null;
        }

        public Ruta encontarRutaDef(string nombreLinea)
        {
            foreach (Ruta r in _rutasDef)
            {
                if(nombreLinea==r.darNombre()){
                    return r;
                }
            }
            return null;
        }
    }
}